package com.yunchang.open.iploaction;

import com.yunchang.open.iploaction.parser.IPv4Parser;
import com.yunchang.open.iploaction.parser.cz88.Cz88Parser;
import com.yunchang.open.iploaction.parser.ipip.IpipParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * IPLocationHelper工厂类。
 * 提供多种创建{@link IPLocationHelper}实例的方法。
 *
 * Created by jasontujun on 2019/7/10.
 */
public class IPLocationFactory {

    private static final Logger logger = LoggerFactory.getLogger(IPLocationFactory.class);

    /**
     * 创建IPLocationHelper新实例。
     * 从项目资源文件目录下加载IP库文件。
     * @param clazz 解析器类型
     * @param fileName IP库文件（相对于项目资源文件目录下）
     * @return 如果文件存在，返回初始化好的实例；否则返回instance
     */
    public static IPLocationHelper createFromResource(Class<? extends IPv4Parser> clazz,
                                                      String fileName) {
        return createFromResource(clazz, fileName, null);
    }

    /**
     * 创建IPLocationHelper实例。
     * 从项目资源文件目录下加载IP库文件。
     * @param clazz 解析器类型
     * @param fileName IP库文件（相对于项目资源文件目录下）
     * @param instance 是否使用已有的实例，不为null，则不创建新实例
     * @return 如果文件存在，返回初始化好的实例；否则返回instance
     */
    public static IPLocationHelper createFromResource(Class<? extends IPv4Parser> clazz,
                                                      String fileName,
                                                      IPLocationHelper instance) {
        ClassLoader loader = IPLocationFactory.class.getClassLoader();
        URL url = loader.getResource(fileName);
        if (url == null) {
            return instance;
        }
        return createFromFile(clazz, new File(url.getFile()), instance);
    }


    /**
     * 通过ip库文件，创建IPLocationHelper新实例。
     * @param clazz 解析器类型
     * @param file IP库文件
     * @return 如果文件存在，返回初始化好的实例；否则返回instance
     */
    public static IPLocationHelper createFromFile(Class<? extends IPv4Parser> clazz,
                                                  File file) {
        return createFromFile(clazz, file, null);
    }

    /**
     * 通过ip库文件，创建IPLocationHelper实例。
     * @param clazz 解析器类型
     * @param file IP库文件
     * @param instance 是否使用已有的实例（不为null，则不会创建新实例）
     * @return 如果文件存在，返回初始化好的实例；否则返回instance
     */
    public static IPLocationHelper createFromFile(Class<? extends IPv4Parser> clazz,
                                                  File file,
                                                  IPLocationHelper instance) {
        if (file == null || !file.exists()) {
            return instance;
        }
        try {
            if (instance == null) {
                return new IPLocationHelper().addParser(clazz, new FileInputStream(file));
            } else {
                return instance.addParser(clazz, new FileInputStream(file));
            }
        } catch (Exception e) {
            logger.error("", e);
            return instance;
        }
    }

    /**
     * 通过http的GET请求远程url的ip库文件，创建IPLocationHelper新实例。
     * @param clazz 解析器类型
     * @param url IP库文件的url地址
     * @return 如果url可以访问且数据存在，返回初始化好的实例；否则返回instance
     */
    public static IPLocationHelper createFromUrl(Class<? extends IPv4Parser> clazz,
                                                 String url) {
        return createFromUrl(clazz, url, null, null);
    }

    /**
     * 通过http的GET请求远程url的ip库文件，创建IPLocationHelper实例。
     * @param clazz 解析器类型
     * @param url IP库文件的url地址
     * @param instance 是否使用已有的实例（不为null，则不会创建新实例）
     * @param backupFile 备用ip库文件（一旦从inputStream初始化失败，会通过backupFile初始化）
     * @return 如果url可以访问且数据存在，返回初始化好的实例；否则返回instance
     */
    public static IPLocationHelper createFromUrl(Class<? extends IPv4Parser> clazz,
                                                 String url,
                                                 IPLocationHelper instance,
                                                 File backupFile) {
        if (url == null || url.isEmpty()) {
            return createFromFile(clazz, backupFile, instance);
        }
        HttpURLConnection request = null;
        try {
            URL requestUrl = new URL(url);
            request = (HttpURLConnection)requestUrl.openConnection();
            request.setRequestMethod("GET");
            request.connect();
            //获取响应码
            int statusCode = request.getResponseCode();
            // 返回码不是2xx，返回空
            if (statusCode < 200 || statusCode >= 300) {
                return createFromFile(clazz, backupFile, instance);
            }
            // 获取ip库的InputStream流
            InputStream inputStream = request.getInputStream();
            return createFromInputStream(clazz, inputStream, instance, backupFile);
        } catch (Exception e) {
            logger.error("", e);
            return createFromFile(clazz, backupFile, instance);
        } finally {
            if (request != null) {
                request.disconnect();
            }
        }
    }

    /**
     * 通过http的GET请求远程url的ip库文件，创建IPLocationHelper实例。
     * 如果http请求失败或初始化失败，会在项目资源文件目录下，用默认的ip库文件初始化：
     * {@link Cz88Parser}会加载qqwry.dat文件，{@link IpipParser}会加载ipdb.datx文件。
     * @param clazz 解析器类型
     * @param url IP库文件的url地址
     * @param instance 是否使用已有的实例（不为null，则不会创建新实例）
     * @return 如果url可以访问且数据存在，返回初始化好的实例；否则返回instance
     */
    public static IPLocationHelper createFromUrlOrDefault(Class<? extends IPv4Parser> clazz,
                                                          String url,
                                                          IPLocationHelper instance) {
        return createFromUrl(clazz, url, instance, getDefaultFile(clazz));
    }

    /**
     * 通过InputStream流，创建IPLocationHelper新实例。
     * @param clazz 解析器类型
     * @param inputStream IP库数据流
     * @return 如果IP库数据流不为空，返回初始化好的新实例；否则返回null
     */
    public static IPLocationHelper createFromInputStream(Class<? extends IPv4Parser> clazz,
                                                         InputStream inputStream) {
        return createFromInputStream(clazz, inputStream, null, null);
    }

    /**
     * 通过InputStream流，创建IPLocationHelper实例。
     * @param clazz 解析器类型
     * @param inputStream IP库数据流
     * @param instance 是否使用已有的实例（不为null，则不会创建新实例）
     * @param backupFile 备用ip库文件（一旦从inputStream初始化失败，会通过backupFile初始化）
     * @return 如果IP库数据流不为空，返回初始化好的实例；否则返回null
     */
    public static IPLocationHelper createFromInputStream(Class<? extends IPv4Parser> clazz,
                                                         InputStream inputStream,
                                                         IPLocationHelper instance,
                                                         File backupFile) {
        if (inputStream == null) {
            return createFromFile(clazz, backupFile, instance);
        }
        try {
            if (instance == null) {
                return new IPLocationHelper().addParser(clazz, inputStream);
            } else {
                return instance.addParser(clazz, inputStream);
            }
        } catch (Exception e) {
            logger.error("", e);
            return createFromFile(clazz, backupFile, instance);
        }
    }

    /**
     * 通过InputStream流，创建IPLocationHelper实例。
     * 如果InputStream流初始化失败，会在项目资源文件目录下，用默认的ip库文件初始化：
     * {@link Cz88Parser}会加载qqwry.dat文件，{@link IpipParser}会加载ipdb.datx文件。
     * @param clazz 解析器类型
     * @param inputStream IP库数据流
     * @param instance 是否使用已有的实例（不为null，则不会创建新实例）
     * @return 如果IP库数据流不为空，返回初始化好的实例；否则返回instance
     */
    public static IPLocationHelper createFromInputStreamOrDefault(Class<? extends IPv4Parser> clazz,
                                                                  InputStream inputStream,
                                                                  IPLocationHelper instance) {
        return createFromInputStream(clazz, inputStream, instance, getDefaultFile(clazz));
    }


    /**
     * 创建IPLocationHelper实例，自带所有默认的ip解析库{@link IpipParser}，{@link Cz88Parser}。
     * 默认在项目资源文件目录下加载qqwry.dat文件和ipdb.datx文件，请确保该文件存在，否则无法创建实例。
     * @return 返回初始化好的新实例；否则返回null
     */
    public static IPLocationHelper createByDefault() {
        IPLocationHelper helper = createFromResource(Cz88Parser.class, "qqwry.dat", null);
        return createFromResource(IpipParser.class, "ipdb.datx", helper);
    }

    /**
     * 创建IPLocationHelper实例，使用{@link Cz88Parser}的ip库的解析。
     * 默认在项目资源文件目录下加载qqwry.dat文件，请确保该文件存在，否则无法创建实例。
     * @return 如果qqwry.dat文件存在，返回初始化好的新实例；否则返回null
     */
    public static IPLocationHelper createCz88ByDefault() {
        return createFromResource(Cz88Parser.class, "qqwry.dat", null);
    }


    /**
     * 创建IPLocationHelper实例，使用{@link IpipParser}的ip库的解析。
     * 默认在项目资源文件目录下加载ipdb.datx文件，请确保该文件存在，否则无法创建实例。
     * @return 如果ipdb.datx文件存在，返回初始化好的新实例；否则返回null
     */
    public static IPLocationHelper createIpipByDefault() {
        return createFromResource(IpipParser.class, "ipdb.datx", null);
    }

    private static File getDefaultFile(Class<? extends IPv4Parser> clazz) {
        File defaultFile = null;
        String defaultFileName = null;
        if (clazz == IpipParser.class) {
            defaultFileName = "ipdb.datx";
        } else if (clazz == Cz88Parser.class) {
            defaultFileName = "qqwry.dat";
        }
        if (defaultFileName != null) {
            ClassLoader loader = IPLocationFactory.class.getClassLoader();
            URL fileUrl = loader.getResource(defaultFileName);
            if (fileUrl != null) {
                defaultFile = new File(fileUrl.getFile());
            }
        }
        return defaultFile;
    }
}
